#include "astr.h"
#include <string.h>

static const size_t astr_enlarging_cap = 32;

astr_t *astr_init(const char *str)
{
    size_t str_len = str == NULL ? 0 : strlen(str);
    size_t cap = str_len + astr_enlarging_cap;
    astr_t *ptr = (astr_t *)malloc(sizeof(astr_t));
    if (ptr == NULL)
        return NULL;
    ptr->cap = cap;
    ptr->len = str_len;
    ptr->data = (char *)malloc(ptr->cap);
    if (ptr->data == NULL)
    {
        free(ptr);
        return NULL;
    }
    memset(ptr->data, 0, ptr->cap);
    if (str_len != 0)
        strncpy(ptr->data, str, str_len);

    return ptr;
}

static inline int __astr_enlarging(astr_t **ptr, size_t enlarging_len)
{
    if (*ptr == NULL || enlarging_len <= 0)
        return -1;
    astr_t *str = *ptr;
    size_t cap = str->cap + enlarging_len;
    char *data = (char *)malloc(cap);
    if (data == NULL)
        return -2;
    // 数据转存
    memset(data, 0, cap);
    strcpy(data, str->data);

    str->cap = cap;
    // 旧数据释放
    free(str->data);
    str->data = data;
    return 0;
}

int astr_add(astr_t *ptr, const char c)
{
    if (ptr == NULL || c == '\0')
        return -1;

    int str_len = 1;
    int enlarging_ret = 0;
    long long need_len = ptr->len + str_len - ptr->cap;
    if (need_len > 0)
        enlarging_ret = __astr_enlarging(&ptr, need_len + astr_enlarging_cap);
    if (enlarging_ret)
        return enlarging_ret;

    ptr->data[ptr->len] = c;
    ptr->len += str_len;
    return 0;
}

int astr_append(astr_t *ptr, const char *str)
{
    if (ptr == NULL || str == NULL)
        return -1;
    size_t str_len = strlen(str);
    if (str_len == 0)
        return -1;
    int enlarging_ret = 0;
    long long need_len = ptr->len + str_len - ptr->cap;
    if (need_len > 0)
        enlarging_ret = __astr_enlarging(&ptr, need_len + astr_enlarging_cap);
    if (enlarging_ret)
        return enlarging_ret;

    strcpy(ptr->data + ptr->len, str);
    ptr->len += str_len;
    return 0;
}

int astr_reset(astr_t *ptr)
{
    if (ptr == NULL)
        return -1;
    memset(ptr->data, 0, ptr->cap);
    ptr->len = 0;
    return 0;
}

void astr_deinit(astr_t **pp)
{
    if (pp == NULL || *pp == NULL)
        return;

    astr_t *ptr = *pp;
    if (ptr->data)
    {
        free(ptr->data);
        ptr->data = NULL;
    }
    if (ptr)
        free(ptr);
    *pp = NULL;
}